﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using Microsoft.Win32.SafeHandles;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;

namespace HiddenWatermark
{
    internal class ImageHelpers
    {
        internal bool ClipSupport { get; set; }

        private delegate double ColorConversion(double red, double green, double blue);
        private const int PaddingLimit = 10;

        private int[] _clippingWidths = new int[] { 1024, 911, 832, 800, 744, 700, 640, 600, 568, 508, 480, 448, 400, 360, 333 };
        private int[] _clippingHeights = new int[] { 768, 683, 624, 600, 558, 525, 480, 450, 426, 373, 360, 336, 300, 270, 250 };

        public ImageHelpers(bool clipSupport, IEnumerable<int> widths, IEnumerable<int> heights)
        {
            ClipSupport = clipSupport;
            if (widths != null)
                _clippingWidths = widths.ToArray();
            if (heights != null)
                _clippingHeights = heights.ToArray();
        }



        public byte[] MergeWatermarkPixels(byte[] fileBytes, byte[] watermarkBytes)
        {
            if (fileBytes == null || fileBytes.Length == 0)
                throw new ArgumentNullException(nameof(fileBytes));
            if (watermarkBytes == null || watermarkBytes.Length == 0)
                throw new ArgumentNullException(nameof(watermarkBytes));

            // 加载主图像并确保其格式为 BGRA32
            using var mainImage = Image.Load<Bgra32>(new MemoryStream(fileBytes));

            int width = mainImage.Width;
            int height = mainImage.Height;

            // 缩放水印图像
            var wmPixels = ScaledWatermarkCache.TryGetScaledWatermark(width, height);
            if (wmPixels == null)
            {
                int paddingW = GetPaddingW(width);
                int paddingH = GetPaddingH(height);

                wmPixels = ScaleWatermark(watermarkBytes, width, height, paddingW, paddingH);
                ScaledWatermarkCache.AddScaledWatermark(width, height, wmPixels);
            }

            // 将水印像素数据转换为 ImageSharp 图像
            using var watermarkImage = new Image<Bgra32>(width, height);
            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    int index = (y * width + x) * 4;
                    watermarkImage[x, y] = new Bgra32(
                        wmPixels[index],
                        wmPixels[index + 1],
                        wmPixels[index + 2],
                        wmPixels[index + 3]
                    );
                }
            }

            // 合并像素数据
            // 每个像素对比和左右的像素，如果颜色不同，就按照一定的算法和水印的颜色值做计算
            Parallel.For(0, height, h =>
            {
                for (int w = 0; w < width; w++)
                {
                    int i = (h * width + w) * 4;
                    var mainPixel = mainImage[w, h];
                    var wmPixel = watermarkImage[w, h];

                    bool nextSame = false, prevSame = false;
                    if (w > 0
                    && mainPixel.R == mainImage[w - 1, h].R
                    && mainPixel.G == mainImage[w - 1, h].G
                    && mainPixel.B == mainImage[w - 1, h].B
                    )
                    {
                        prevSame = true;
                    }
                    if (w + 1 < width
                    && mainPixel.R == mainImage[w + 1, h].R
                    && mainPixel.G == mainImage[w + 1, h].G
                    && mainPixel.B == mainImage[w + 1, h].B
                    )
                    {
                        nextSame = true;
                    }

                    if (!nextSame || !prevSame)
                    {
                        mainImage[w, h] = new Bgra32(
                            ToByte(mainPixel.R + 128 - wmPixel.R),
                            ToByte(mainPixel.G + 128 - wmPixel.G),
                            ToByte(mainPixel.B + 128 - wmPixel.B),
                            mainPixel.A // 保持 Alpha 通道不变
                        );
                    }
                }
            });

            using (FileStream fs = new FileStream(@"D:\ywd\work\ncs\新水印的尝试\blind_watermark\blind_watermark\examples\pic\mainResult.png", FileMode.OpenOrCreate))
            {
                mainImage.SaveAsJpeg(fs);
            }

            // 创建一个 MemoryStream 来保存图像数据
            using (MemoryStream ms = new MemoryStream())
            {
                // 选择一个编码器，这里以 Png 编码器为例
                IImageEncoder encoder = new JpegEncoder();


                // 将图像写入 MemoryStream
                encoder.Encode(mainImage, ms);

                // 从 MemoryStream 中获取字节数组
                byte[] imageBytes = ms.ToArray();

                return imageBytes;

                // 现在 imageBytes 包含了图像的二进制数据
            }

            // 保存结果图像
            return ToByteArray(mainImage);
        }

        public static byte[] ToBgr32(byte[] imageBytes)
        {
            if (imageBytes == null || imageBytes.Length == 0) return null;

            using var ms = new MemoryStream(imageBytes);
            using var image = Image.Load<Bgra32>(ms); // 直接加载为 BGRA32 格式

            int width = image.Width;
            int height = image.Height;
            int stride = width * 4; // 每个像素占 4 字节 (BGRA)

            byte[] pixelData = new byte[height * stride];

            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    var pixel = image[x, y];
                    int index = y * stride + x * 4;
                    pixelData[index] = pixel.B;   // Blue
                    pixelData[index + 1] = pixel.G; // Green
                    pixelData[index + 2] = pixel.R; // Red
                    pixelData[index + 3] = pixel.A; // Alpha
                }
            }

            return pixelData;
        }



        private static byte[] ToByteArray(Image<Bgra32> image)
        {

            int width = image.Width;
            int height = image.Height;
            int pixelSize = 4; // 每个像素占 4 字节 (BGRA)

            byte[] pixels = new byte[height * width * pixelSize];
            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    var pixel = image[x, y];
                    int index = (y * width + x) * pixelSize;
                    pixels[index] = pixel.B;   // Blue
                    pixels[index + 1] = pixel.G; // Green
                    pixels[index + 2] = pixel.R; // Red
                    pixels[index + 3] = pixel.A; // Alpha
                }
            }

            return pixels;
        }



        public static byte[] ScaleWatermark(byte[] watermarkBytes, int width, int height, int paddingW, int paddingH)
        {
            if (watermarkBytes == null || watermarkBytes.Length == 0)
                throw new ArgumentNullException(nameof(watermarkBytes));

            using var ms = new MemoryStream(watermarkBytes);
            using var originalImage = Image.Load<Bgra32>(ms);

            int newWidth = width + paddingW;
            int newHeight = height + paddingH;

            // 创建新的空白图像
            using var scaledImage = new Image<Bgra32>(newWidth, newHeight);

            // 计算水印放置的位置
            int startX = (newWidth - width) / 2;
            int startY = (newHeight - height) / 2;

            // 将原始水印复制到新的图像上
            originalImage.Mutate(ctx => ctx.Resize(width, height));
            scaledImage.Mutate(ctx => ctx.DrawImage(originalImage, new Point(startX, startY), 1f));

            // 提取指定区域的像素数据
            using var extractedImage = scaledImage.Clone(ctx => ctx.Crop(new Rectangle(startX, startY, width, height)));
            return ToByteArray(extractedImage);
        }

        //private static byte ToByte(double value)
        //{
        //    // 将归一化后的值 [0, 1] 转换回 [0, 255]
        //    return (byte)Math.Clamp(value * 255, 0, 255);
        //}

        private byte ToByte(double input)
        {
            if (input < 0)
                return 0;
            if (input > 255)
                return 255;
            return (byte)input;

        }

        public byte[] SavePixels(RgbData data)
        {
            if (data == null || data.R == null || data.G == null || data.B == null)
                throw new ArgumentNullException(nameof(data));

            int width = data.R.GetLength(0);
            int height = data.R.GetLength(1);
            //var pixelSize = 4;

            //byte[] pixels = new byte[width * pixelSize * height];
            //Parallel.For(0, height, h =>
            //{
            //    var hPos = h * width * pixelSize;
            //    for (int w = 0; w < width; w++)
            //    {
            //        var i = hPos + (w * pixelSize);

            //        pixels[i] = ToByte(data.B[w, h]);
            //        pixels[i + 1] = ToByte(data.G[w, h]);
            //        pixels[i + 2] = ToByte(data.R[w, h]);
            //    }
            //});


            using var image = new Image<Bgra32>(width, height);

            // 使用 Memory<T> 和 Span<T> 高效访问像素数据
            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    byte r = ToByte(data.R[x, y]);
                    byte g = ToByte(data.G[x, y]);
                    byte b = ToByte(data.B[x, y]);

                    image[x, y] = new Bgra32(b, g, r, 0); // 设置 Alpha 通道为 255（完全不透明）
                }
            }


            using (MemoryStream ms = new MemoryStream())
            {
                // 选择一个编码器，这里以 Png 编码器为例
                IImageEncoder encoder = new JpegEncoder();


                // 将图像写入 MemoryStream
                encoder.Encode(image, ms);

                // 从 MemoryStream 中获取字节数组
                byte[] imageBytes = ms.ToArray();




                return imageBytes;

                // 现在 imageBytes 包含了图像的二进制数据
            }

            //using var ms = new MemoryStream();
            //image.SaveAsJpeg(ms);
            //var temp = ms.ToArray();
            //return temp;
        }


        public double[,] ExtractWatermarkData(byte[] bytes, int width, int height)
        {
            if (bytes == null || bytes.Length == 0)
            {
                throw new ArgumentNullException(nameof(bytes));
            }

            using var ms = new MemoryStream(bytes);
            using var image = Image.Load<Bgra32>(ms);

            // 计算 padding
            int paddingW = GetPaddingW(image.Width);
            int paddingH = GetPaddingH(image.Height);

            // 计算新的尺寸
            int newWidth = (int)Math.Round((image.Width / (double)(image.Width + paddingW)) * width);
            int newHeight = (int)Math.Round((image.Height / (double)(image.Height + paddingH)) * height);

            // 调整图像大小
            image.Mutate(ctx => ctx.Resize(newWidth, newHeight));

            //image.Save(@"D:\ywd\work\ncs\新水印的尝试\blind_watermark\blind_watermark\examples\pic\diff2.jpg");

            // 读取像素数据并进行颜色空间转换
            return ReadPixels(image, ColorSpaceConversion.RgbToU);
        }

        private double[,] ReadPixels(Image<Bgra32> image, ColorConversion convert)
        {
            int width = image.Width;
            int height = image.Height;
            double[,] result = new double[width, height];

            for (int h = 0; h < height; h++)
            {
                for (int w = 0; w < width; w++)
                {
                    Bgra32 pixel = image[w, h];

                    var red = pixel.R;
                    var green = pixel.G;
                    var blue = pixel.B;

                    //从255修改为真实red后，提取出来的相似度下降了
                    result[w, h] = convert(255, green, blue);
                    //result[w, h] = convert(red, green, blue);
                }
            }

            return result;
        }

        public static byte[] CreateImage(byte[] bytes, int decodePixelWidth = 0, int decodePixelHeight = 0)
        {
            if (bytes == null || bytes.Length == 0) return null;

            using var ms = new MemoryStream(bytes);
            using var image = Image.Load<Rgba32>(ms);

            // 如果指定了解码宽度或高度，则调整图像大小
            if (decodePixelWidth > 0 || decodePixelHeight > 0)
            {
                image.Mutate(x => x.Resize(new Size(decodePixelWidth, decodePixelHeight)));
            }

            // 将 ImageSharp 图像保存为 PNG 格式的字节数组
            using var outputMs = new MemoryStream();
            image.SaveAsPng(outputMs);
            return outputMs.ToArray();
        }

        private int GetPaddingW(int width)
        {
            if (!ClipSupport) return 0;

            int padding = 0;
            for (int i = 0; i < _clippingWidths.Length; i++)
            {
                if (width > _clippingWidths[i] + PaddingLimit)
                {
                    padding = i == 0 ? 0 : _clippingWidths[i - 1] - width;
                    break;
                }
            }

            return Math.Max(0, padding);
        }

        private int GetPaddingH(int height)
        {
            if (!ClipSupport) return 0;

            int padding = 0;
            for (int i = 0; i < _clippingHeights.Length; i++)
            {
                if (height > _clippingHeights[i] + PaddingLimit)
                {
                    padding = i == 0 ? 0 : _clippingHeights[i - 1] - height;
                    break;
                }
            }

            return Math.Max(0, padding);
        }

        public RgbData ReadPixels(byte[] imageBytes)
        {
            if (imageBytes == null || imageBytes.Length == 0) return null;

            using var ms = new MemoryStream(imageBytes);
            using var image = Image.Load<Rgba32>(ms);

            int width = image.Width;
            int height = image.Height;

            // 创建二维数组来存储 R、G、B 通道的数据
            double[,] R = new double[width, height];
            double[,] G = new double[width, height];
            double[,] B = new double[width, height];

            // 使用并行处理来提高性能
            Parallel.For(0, height, h =>
            {
                for (int w = 0; w < width; w++)
                {
                    Rgba32 pixel = image[w, h];
                    byte red = pixel.R;
                    byte green = pixel.G;
                    byte blue = pixel.B;

                    //// 将颜色值转换为 double 类型并归一化（可选）
                    //R[w, h] = red / 255.0;
                    //G[w, h] = green / 255.0;
                    //B[w, h] = blue / 255.0;
                    // 将颜色值转换为 double 类型并归一化（可选）
                    R[w, h] = red;
                    G[w, h] = green;
                    B[w, h] = blue;
                }
            });

            return new RgbData(R, G, B);
        }
        private double[,] ReadPixels(byte[] imageBytes, ColorConversion convert)
        {
            if (imageBytes == null || imageBytes.Length == 0) return null;

            using var ms = new MemoryStream(imageBytes);
            using var image = Image.Load<Rgba32>(ms);

            int width = image.Width;
            int height = image.Height;

            // 创建一个二维数组来存储转换后的像素值
            double[,] data = new double[width, height];

            // 使用并行处理来提高性能
            Parallel.For(0, height, h =>
            {
                for (int w = 0; w < width; w++)
                {
                    Rgba32 pixel = image[w, h];
                    byte alpha = pixel.A;
                    byte green = pixel.G;
                    byte blue = pixel.B;

                    // 调用颜色转换委托
                    data[w, h] = convert(alpha, green, blue);
                }
            });

            return data;
        }

    }
}